#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <set>

namespace part1
{

static std::vector<std::size_t> getNumbers(const std::string& line)
{
    std::size_t index{0};
    std::vector<std::size_t> numbers{};

    while (index < line.size())
    {
        if (line[index] == ' ')
        {
            index += 1;
            continue;
        }

        // Find next sapce
        std::size_t pos{index + 1};
        while (true)
        {
            if (pos >= line.size() || line[pos] == ' ')
                break;
            pos += 1;
        }

        // line[index, pos) is digit
        numbers.push_back(std::stoull(&line[index]));
        index = pos + 1;
    }
    return numbers;
}

class Map 
{
    struct Record
    {
        Record(std::size_t ds, std::size_t ss, std::size_t rl) :
            destinationStart{ds},
            sourceStart{ss},
            rangeLength{rl}
        {
        }
        std::size_t destinationStart;
        std::size_t sourceStart;
        std::size_t rangeLength;
    };

public:
    Map() = default;
    Map(const std::vector<std::string>& content) : _records{}
    {
        // Each line is a record!
        for (const std::string& line : content)
        {        
            std::vector<std::size_t> numbers{getNumbers(line)};
            if (numbers.size() != 3)
            {
                std::cout << "Record should containes 3 numbers." << std::endl;
                std::exit(-1);
            }

            this->_records.emplace_back(numbers[0], numbers[1], numbers[2]);
        }
    }

    Map(Map&&) = default;

    std::size_t getDestination(std::size_t source) const
    {
        // Try each record
        for (const Record& record : this->_records)
        {
            std::size_t sourceStart{ record.sourceStart };
            std::size_t sourceEnd{ record.sourceStart + record.rangeLength };
            if (source >= sourceStart && source < sourceEnd)
                return source + record.destinationStart - sourceStart;
        }
        return source;
    }

    void print() const
    {
        for (const Record& record : this->_records)
        {
            std::cout << record.destinationStart << ' '
                      << record.sourceStart << ' '
                      << record.rangeLength << std::endl;
        }
        std::cout << std::endl;
    }

private:
    std::vector<Record> _records;
};

std::vector<std::size_t> getTargetSeeds(const std::string& line)
{
    return getNumbers(line.substr(7, line.size() - 7));
}

std::map<std::string, Map> getMaps(std::ifstream& file)
{
    std::map<std::string, Map> maps{};

    auto isdigit = [] (char ch) { return ch >= '0' && ch <= '9'; };
    std::string line;
    while (std::getline(file, line))
    {
        if (line.empty()) continue;
        if (!isdigit(line.front()))
        {
            std::size_t pos{ line.find("map") };
            std::string name{ line.substr(0, pos - 1) };

            std::vector<std::string> content{};
            while ( std::getline(file, line) )
            {
                if (line.empty()) break;
                content.emplace_back( std::move(line) );
            }
        
            maps.emplace(std::move(name), Map{content});
        }
    }

    return maps;
}

int work() 
{
    std::ifstream file{"./src/day05/input.txt"};
    std::string line;

    // Get Target seeds
    std::getline(file, line);
    std::vector<std::size_t> targetSeeds{getTargetSeeds(line)};
    std::cout << targetSeeds.size() << std::endl;

    // Get all maps
    std::map<std::string, Map> maps{getMaps(file)};

    // Get Result
    std::vector<std::size_t> locations{};
    for (std::size_t seed : targetSeeds)
    {
        std::size_t soil{ maps["seed-to-soil"].getDestination(seed) };
        std::size_t fertilizer{ maps["soil-to-fertilizer"].getDestination(soil) };
        std::size_t water{ maps["fertilizer-to-water"].getDestination(fertilizer) };
        std::size_t light{ maps["water-to-light"].getDestination(water) };
        std::size_t temperature{ maps["light-to-temperature"].getDestination(light) };
        std::size_t humidity{ maps["temperature-to-humidity"].getDestination(temperature) };
        std::size_t location{ maps["humidity-to-location"].getDestination(humidity) };
        
        // std::cout << seed << ' ' << soil << ' ' 
        //           << fertilizer << ' ' << water << ' ' 
        //           << light << ' ' << temperature << ' ' 
        //           << humidity << ' ' << location << std::endl;
        locations.emplace_back(location);
    }

    std::size_t minLocation{locations[0]};
    for (std::size_t location : locations)
    {
        if (location < minLocation)
            minLocation = location;
    }

    std::cout << minLocation << std::endl;

    return 0;
}

}